package com.youfang.sixthmedia.net;

import org.json.JSONObject;

import android.util.Log;
import ch.boye.httpclientandroidlib.Header;

/**
 * 对所有发往服务器的 响应消息 的抽象
 * 
 * @author teddy
 * 
 */
public abstract class Response implements Message {

	protected final String TAG = this.getClass().getSimpleName();

	// 客户端失败（比如网络异常等）
	public static final int CODE_CLIENT_ERROR = -1;

	// 通用失败
	public static final int CODE_SUCCESS = 1;

	// 成功
	public static final int CODE_ERROR = 0;

	public static final int ERROR_SESSION_TIME_OUT = 1005; // 会话过期
	public static final int CODE_AUTHORIZE_DENY = 9;// 未登录

	public static final int LOGIN_SUCCESS = 1002;// 表示登录成功
	public static final int LOGIN_PASSWORD_ERROR = 1003;// 表示密码错误
	public static final int USER_EXISTED = 1004;// 表示帐号不存在
	public static final int USER_REGED = 1006;// 表示帐号已经注册过

	// 错误码
	public static final int ERROR_CODE_SUCESS = 0;
	public static final int ERROR_CODE_AUTHORIZE_DENY = 9;

	protected String error = "";

	protected int retcode = Integer.MAX_VALUE;
	protected int errorcode = ERROR_CODE_SUCESS;

	// 客户端异常
	protected boolean exception;
	protected String exceptionMsg;
	protected int exceptionStatusCode;// 出现异常时http的状态码

	private int type;

	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}

	public boolean isFakeMessage() {
		return this.getType() == TYPE_FAKE;
	}

	// 判断服务端的服务端点（URL）是否已经存在。
	// 在客户端升级后，有新的请求接口，但是服务端没升级的情况,http下得到的404错误
	// 如果该请求端点对业务的执行来说不是必须的，则网络层可以选择继续处理不报错。
	public boolean isServerEndPointMissing() {
		return exceptionStatusCode == 404;
	}

	public boolean shouldShowLoginWhenNeedFailAuthed() {
		return true;
	}

	public OpResult validate() {
		return OpResult.OK;
	}

	public int getExceptionStatusCode() {
		return exceptionStatusCode;
	}

	public void setExceptionStatusCode(int exceptionStatusCode) {
		this.exceptionStatusCode = exceptionStatusCode;
	}

	// 处理http头
	protected void processHeader(Header... header) {

	}

	public void decode(byte[] msgBytes) throws Exception {

		// 调用出现异常，则不继续解析消息体
		if (this.isException()) {
			throw new MessageException("消息错误：" + error);
		}

		// 分析消息
		JSONObject json = this.analyze(msgBytes);

		// 是否成功
		if (this.isOk()) {
			this.decodeBody(json);
		}

	}

	public JSONObject analyze(byte[] msgBytes) throws Exception {

		// http://lavasoft.blog.51cto.com/62575/331095 BOM问题
		String msgString = new String(detectBOM(msgBytes), "UTF-8");
		Log.i("HttpRemoter", "返回消息(" + this.descName() + "):" + msgString);
		JSONObject json = new JSONObject(msgString);
		if (json.has("retcode")) {
			this.retcode = json.getInt("retcode");
		} else {
			// In case no retcode found ,we choose errorcode to determine the
			// result status
			if (json.has("errorcode")) {
				this.errorcode = json.getInt("errorcode");
			}
		}

		return json;
	}

	// 检测BOM（Byte ORDER MARK）
	protected byte[] detectBOM(byte[] buffer) {
		if ((buffer[0] == (byte) 0xEF) && (buffer[1] == (byte) 0xBB)
				&& (buffer[2] == (byte) 0xBF)) {
			byte[] ret = new byte[buffer.length - 3];
			System.arraycopy(buffer, 3, ret, 0, ret.length);
			return ret;
		}
		return buffer;
	}

	// 子类负责解析消息体,也就是消息中的data域
	protected abstract void decodeBody(JSONObject bodyJSON) throws Exception;

	public boolean isSessionTimeout() {
		return this.retcode == ERROR_SESSION_TIME_OUT;

	}

	public boolean isOk() {

		// choose errorcode or retcode according to the psudo code
		// "Integer.MAX_VALUE"
		return this.retcode == Integer.MAX_VALUE ? this.errorcode == ERROR_CODE_SUCESS
				: this.retcode == CODE_SUCCESS;
	}

	public boolean isFail() {
		return !this.isOk();
	}

	public String getError() {
		return error;
	}

	public void setError(String error) {
		this.error = error;
	}

	public boolean isException() {
		return exception;
	}

	public void setException(boolean exception) {
		this.exception = exception;
	}

	public String getExceptionMsg() {
		return exceptionMsg;
	}

	public void setExceptionMsg(String exceptionMsg) {
		this.exceptionMsg = exceptionMsg;
	}

	public void raiseException(String msg) {
		this.retcode = CODE_CLIENT_ERROR;
		this.error = msg;
		this.exception = true;
		this.exceptionMsg = msg;
	}

	// factory method
	public static Response common() {
		return new CommonResponse();
	}

	// //////////////////tools/////////////////////
	public String descName() { // used in log...
		String name = this.getClass().getName();
		int dot = name.lastIndexOf('.');
		if (dot != -1) {
			return name.substring(dot + 1);
		}
		return name;

	}

	public static String getString(JSONObject o, String key) throws Exception {

		if (!o.has(key) || o.isNull(key)) {
			return null;
		}

		return o.getString(key);

	}

	public static long getLong(JSONObject o, String key) throws Exception {

		if (!o.has(key) || o.isNull(key)) {
			return 0;
		}

		return o.getLong(key);

	}

	public static int getInt(JSONObject o, String key) throws Exception {

		if (!o.has(key) || o.isNull(key)) {
			return 0;
		}

		return o.getInt(key);

	}

	public static double getDouble(JSONObject o, String key) throws Exception {

		if (!o.has(key) || o.isNull(key)) {
			return 0;
		}

		return o.getDouble(key);

	}

	public static boolean getBoolean(JSONObject o, String key) throws Exception {

		if (!o.has(key) || o.isNull(key)) {
			return false;
		}

		return o.getBoolean(key);

	}

}
